﻿using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyModel;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.SwaggerUI;
using System.Reflection;
using CTP.CRM.Core.Swagger.Options;
using CTP.CRM.Core.Swagger.Filter;
using System.Text.Json;

namespace CTP.CRM.Core.Swagger
{
    //
    // 摘要:
    //     swagger启动拓展类
    public static class SwaggerStartupExtensions
    {
        private static SwaggerOptions _swaggerOptions = null;

        private static Dictionary<string, string> _groupDic = new Dictionary<string, string>();

        public static IServiceCollection AddSwagger(this IServiceCollection services)
        {
            IConfiguration configuration = services.GetConfiguration();
            IWebHostEnvironment webHostEnvironment = services.GetHostingEnvironment();
            LogHelper.Debug("-- 开始注入Swagger服务", false);
            _swaggerOptions = configuration.GetSection("Swagger").Get<SwaggerOptions>();
            if (!_swaggerOptions.IsEnable)
            {
                LogHelper.Debug("-- 关闭Swagger", false);
            }

            List<string> _groupList = GetAllApiGroup(_swaggerOptions.ScanPrefix).ToList();
            LogHelper.Debug("-- 启用Swagger", false);
            services.AddSwaggerGen(delegate (SwaggerGenOptions options)
            {
                foreach (string item in _groupList)
                {
                    options.SwaggerDoc(item, new OpenApiInfo
                    {
                        Version = "v1",
                        Title = item
                    });
                }

                IEnumerable<string> xmlFileByPrefix = GetXmlFileByPrefix(_swaggerOptions.ScanPrefix);
                foreach (string item2 in xmlFileByPrefix)
                {
                    options.IncludeXmlComments(Path.Combine(webHostEnvironment.ContentRootPath, item2), includeControllerXmlComments: true);
                }

                options.CustomOperationIds((ApiDescription e) => $"{(string.IsNullOrEmpty(e.ActionDescriptor.RouteValues["area"]) ? "" : (e.ActionDescriptor.RouteValues["area"] + "_"))}{e.ActionDescriptor.RouteValues["controller"]}_{e.ActionDescriptor.RouteValues["action"]}_{e.HttpMethod}");
                options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    Description = "JWT授权数据将在请求头中进行传输，在下方输入Bearer {token} 即可，注意两者之间有空格",
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey
                });
                options.AddSecurityRequirement(new OpenApiSecurityRequirement {
            {
                new OpenApiSecurityScheme
                {
                    Reference = new OpenApiReference
                    {
                        Id = "Bearer",
                        Type = ReferenceType.SecurityScheme
                    }
                },
                Array.Empty<string>()
            } });
                List<string> list = (from m in GetAssamblysByPrefix(_swaggerOptions.ScanPrefix)
                                     select m.Name).ToList();
                options.DocumentFilter<SwaggerEnumDocumentFilter>(new object[1] { list });
            });
            return services;
        }

        public static IServiceCollection AddSwagger(this IServiceCollection services, Action<SwaggerGenOptions> swaggerOptions)
        {
            Action<SwaggerGenOptions> swaggerOptions2 = swaggerOptions;
            IConfiguration configuration = services.GetConfiguration();
            IWebHostEnvironment webHostEnvironment = services.GetHostingEnvironment();
            LogHelper.Debug("-- 开始注入Swagger服务", false);
            _swaggerOptions = configuration.GetSection("Swagger").Get<SwaggerOptions>();
            if (!_swaggerOptions.IsEnable)
            {
                LogHelper.Debug("-- 关闭Swagger", false);
            }

            List<string> _groupList = GetAllApiGroup(_swaggerOptions.ScanPrefix).ToList();
            LogHelper.Debug("-- 启用Swagger", false);
            services.AddSwaggerGen(delegate (SwaggerGenOptions options)
            {
                foreach (string item in _groupList)
                {
                    options.SwaggerDoc(item, new OpenApiInfo
                    {
                        Version = "v1",
                        Title = item
                    });
                }

                IEnumerable<string> xmlFileByPrefix = GetXmlFileByPrefix(_swaggerOptions.ScanPrefix);
                foreach (string item2 in xmlFileByPrefix)
                {
                    options.IncludeXmlComments(Path.Combine(webHostEnvironment.ContentRootPath, item2), includeControllerXmlComments: true);
                }

                options.CustomOperationIds((ApiDescription e) => $"{(string.IsNullOrEmpty(e.ActionDescriptor.RouteValues["area"]) ? "" : (e.ActionDescriptor.RouteValues["area"] + "_"))}{e.ActionDescriptor.RouteValues["controller"]}_{e.ActionDescriptor.RouteValues["action"]}_{e.HttpMethod}");
                options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    Description = "JWT授权数据将在请求头中进行传输，在下方输入Bearer {token} 即可，注意两者之间有空格",
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey
                });
                options.AddSecurityRequirement(new OpenApiSecurityRequirement {
            {
                new OpenApiSecurityScheme
                {
                    Reference = new OpenApiReference
                    {
                        Id = "Bearer",
                        Type = ReferenceType.SecurityScheme
                    }
                },
                Array.Empty<string>()
            } });
                List<string> list = (from m in GetAssamblysByPrefix(_swaggerOptions.ScanPrefix)
                                     select m.Name).ToList();
                options.DocumentFilter<SwaggerEnumDocumentFilter>(new object[1] { list });
                swaggerOptions2(options);
            });
            return services;
        }

        public static IApplicationBuilder UseAbpSwaggerUI(this IApplicationBuilder app, IWebHostEnvironment env)
        {
            IWebHostEnvironment env2 = env;
            if (!_swaggerOptions.IsEnable)
            {
                LogHelper.Debug("-- Swagger已关闭", false);
                return app;
            }

            LogHelper.Debug("-- 启用Swagger，" + (string.IsNullOrWhiteSpace(_swaggerOptions.JsonUrlPrefix) ? "未启用域名前缀" : ("域名前缀：" + _swaggerOptions.JsonUrlPrefix)), false);
            app.UseSwagger(delegate (Swashbuckle.AspNetCore.Swagger.SwaggerOptions c)
            {
                if (!env2.IsDevelopment() && !string.IsNullOrEmpty(_swaggerOptions.ServiceUrl))
                {
                    LogHelper.Debug("-- 配置 Swagger 页面请求地址：" + _swaggerOptions.ServiceUrl, false);
                    c.PreSerializeFilters.Add(delegate (OpenApiDocument swagger, HttpRequest httpReq)
                    {
                        string[] array3 = null;
                        try
                        {
                            array3 = JsonSerializer.Deserialize<string[]>(_swaggerOptions.ServiceUrl);
                        }
                        catch
                        {
                            array3 = new string[1] { _swaggerOptions.ServiceUrl };
                        }

                        if (array3 != null && array3.Length != 0)
                        {
                            HashSet<string> source = array3.ToHashSet();
                            swagger.Servers = source.Select((string x) => new OpenApiServer
                            {
                                Url = x
                            }).ToList();
                        }
                    });
                }
            });
            app.UseSwaggerUI(delegate (SwaggerUIOptions options)
            {
                string[] array = new string[1] { string.Empty };
                if (!env2.IsDevelopment())
                {
                    if (string.IsNullOrWhiteSpace(_swaggerOptions.JsonUrlPrefix))
                    {
                        LogHelper.Warn("发现JsonUrlPrefix没有配置，请留意", false);
                        array = new string[1] { string.Empty };
                    }
                    else
                    {
                        array = _swaggerOptions.JsonUrlPrefix.Split(",");
                    }

                    if (_swaggerOptions.JsonUrlPrefix.TrimStart()[0] == '[')
                    {
                        LogHelper.Warn("要配置JsonUrlPrefix 数组建议使用逗号分割，后续建删除json 数组配置方式", false);
                        array = JsonSerializer.Deserialize<string[]>(_swaggerOptions.JsonUrlPrefix);
                    }
                }

                foreach (KeyValuePair<string, string> item in _groupDic)
                {
                    string[] array2 = array;
                    foreach (string text in array2)
                    {
                        options.SwaggerEndpoint(text + item.Value, item.Key);
                    }
                }

                options.RoutePrefix = string.Empty;
                options.DocExpansion(DocExpansion.None);
                options.DefaultModelExpandDepth(5);
            });
            return app;
        }

        private static IEnumerable<CompilationLibrary> GetAssamblysByPrefix(string[] scanPrefix)
        {
            string[] scanPrefix2 = scanPrefix;
            return DependencyContext.Default.CompileLibraries.Where((CompilationLibrary m) => scanPrefix2.Contains(m.Name.Split('.')[0]));
        }

        //
        // 摘要:
        //     根据前缀，扫描加载swagger所需xml文件
        //
        // 参数:
        //   scanPrefix:
        private static IEnumerable<string> GetXmlFileByPrefix(string[] scanPrefix)
        {
            string[] scanPrefix2 = scanPrefix;
            List<string> list = new List<string>();
            List<string> list2 = (from m in GetAssamblysByPrefix(scanPrefix2)
                                  where scanPrefix2.Contains(m.Name.Split('.')[0])
                                  select m.Name + ".xml").ToList();
            foreach (string item in list2)
            {
                string text = Path.Combine(AppContext.BaseDirectory, item);
                if (File.Exists(text))
                {
                    list.Add(text);
                }
            }

            return list;
        }

        private static IEnumerable<string> GetAllApiGroup(string[] scanPrefix)
        {
            string[] scanPrefix2 = scanPrefix;
            List<Assembly> source = (from m in DependencyContext.Default.RuntimeLibraries
                                     where scanPrefix2.Contains(m.Name.Split('.')[0])
                                     select Assembly.Load(m.Name)).ToList();
            List<Type> list = source.SelectMany((Assembly m) => from n in m.GetTypes()
                                                                where PublicTools.IsApiController(n)
                                                                select n).ToList();
            foreach (Type item in list)
            {
                string text = item.GetCustomAttribute<ApiExplorerSettingsAttribute>(inherit: true)?.GroupName;
                if (!string.IsNullOrEmpty(text) && !_groupDic.Keys.Contains(text))
                {
                    _groupDic.Add(text, "/swagger/" + text + "/swagger.json");
                }
            }

            List<MethodInfo> list2 = list.SelectMany((Type m) => from x in m.GetMethods()
                                                                 where PublicTools.IsApiAction(x, m)
                                                                 select x).ToList();
            foreach (MethodInfo item2 in list2)
            {
                string text2 = item2.GetCustomAttribute<ApiExplorerSettingsAttribute>(inherit: true)?.GroupName;
                if (!string.IsNullOrEmpty(text2) && !_groupDic.Keys.Contains(text2))
                {
                    _groupDic.Add(text2, "/swagger/" + text2 + "/swagger.json");
                }
            }

            return _groupDic.Keys.ToList();
        }
    }
}
